package com.example.myserver.es.service;


import com.example.myserver.es.entity.ESProperty;
import com.example.myserver.es.entity.EsItem;
import com.example.myserver.es.entity.excel.*;
import com.example.myserver.util.ExcelUtil;
import com.example.myserver.util.JacksonUtil;
import com.fasterxml.jackson.core.type.TypeReference;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.frameworkset.elasticsearch.client.ClientInterface;
import org.frameworkset.elasticsearch.entity.MetaMap;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Service;

import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.example.myserver.es.enums.EsFileName.*;
import static java.util.stream.Collectors.toList;

/**
 * @author by Jiang Xiaomin
 * @desrc:
 */
@Service
public class EsService implements ApplicationRunner {

    public static final Path DATA_ADDRESS = Paths.get(System.getProperty("user.dir"), "datas");

    public static final String FILE_SUFFIX = ".csv";

    private static final String INDEX = "1858748199830560768_v1_item";
    
    public static final String INDEX_SUFFIX = "v1";
    
    public static Map<String, ItemCategory> categoryMap = Maps.newHashMap();
    

    @Autowired
    private ClientInterface clientInterface;
    
    public void readCacheData() {
        final Path categoryFile = DATA_ADDRESS.resolve(CATEGORIES.name().concat(FILE_SUFFIX));
        final List<ItemCategory> categories = ExcelUtil.readExcel(categoryFile, ItemCategory.class);
        categoryMap = categories.stream().collect(Collectors.toMap(ItemCategory::getId, Function.identity()));
        final Path repositoryFile = DATA_ADDRESS.resolve(REPOSITORIES.name().concat(FILE_SUFFIX));
        final List<ItemRepository> repositories = ExcelUtil.readExcel(repositoryFile, ItemRepository.class);
        final Path itemFile = DATA_ADDRESS.resolve(ITEMS.name().concat(FILE_SUFFIX));
        final List<Item> items = ExcelUtil.readExcel(itemFile, Item.class);
        final Path propertyFile = DATA_ADDRESS.resolve(PROPERTIES.name().concat(FILE_SUFFIX));
        final List<ItemProperties> properties = ExcelUtil.readExcel(propertyFile, ItemProperties.class);
        final Path compositionFile = DATA_ADDRESS.resolve(COMPOSITIONS.name().concat(FILE_SUFFIX));
        final List<ItemComposition> compositions = ExcelUtil.readExcel(compositionFile, ItemComposition.class);
/*        final Path referenceFile = DATA_ADDRESS.resolve(REFERENCES.name().concat(FILE_SUFFIX));
        final List<ItemReference> references = ExcelUtil.readExcel(referenceFile, ItemReference.class);
        final Path handleFile = DATA_ADDRESS.resolve(HANDLES.name().concat(FILE_SUFFIX));
        final List<ItemHandle> handles = ExcelUtil.readExcel(handleFile, ItemHandle.class);*/
        Map<String, List<EsItem>> esItemIndexDocument = getEsItemIndexDocument(categories, repositories, items, properties, compositions);
        esItemIndexDocument.forEach(this:: createItemIndex);
    }
    

    private Map<String, List<EsItem>> getEsItemIndexDocument(final List<ItemCategory> categories,
                                                                    final List<ItemRepository> repositories,
                                                                    final List<Item> items,
                                                                    final List<ItemProperties> properties,
                                                                    final List<ItemComposition> compositions) {
        final Map<String, List<EsItem>> itemDocumentMap = Maps.newHashMap();
        final Map<String, ItemProperties> propertiesMap = properties.stream()
                .collect(Collectors.toMap(ItemProperties::getItemId, itemProperties -> itemProperties, (k1, k2) -> k1));
        repositories.forEach(repository -> {
            final String repositoryId = repository.getId();
            final String repositoryVersion = repositoryId.concat("_").concat(INDEX_SUFFIX);
            final List<Item> itemFilter = items.stream()
                    .filter(item -> repositoryVersion.equals(item.getRepositoryVersion()))
                    .collect(Collectors.toList());
            
            List<EsItem> esItems = itemFilter.stream()
                    .map(item -> getEsItemDocument(item, categories, propertiesMap, compositions))
                    .collect(Collectors.toList());
            itemDocumentMap.put(repositoryVersion.concat("_item"), esItems);
        });
        return itemDocumentMap;
    }

    private EsItem getEsItemDocument(final Item item, 
                                     final List<ItemCategory> categories, 
                                     final Map<String, ItemProperties> propertiesMap,
                                     final List<ItemComposition> compositions) {
        final EsItem esItem = new EsItem();
        esItem.setId(item.getId());
        esItem.setTag(item.getTag());
        /*final List<String> comps = getItemCompositions(item, compositions);
        esItem.setCompositions(comps);*/
        final List<String> cgs =  getItemCategories(item, categories);
        esItem.setCategories(cgs);
        final Map<String, ESProperty> esProperties =  getItemProperties(propertiesMap.getOrDefault(item.getId(), null));
        esItem.setProperties(esProperties);
        return esItem;
    }

    private  Map<String, ESProperty> getItemProperties(final ItemProperties properties) {
        final Map<String, ESProperty> esProperties = Maps.newHashMap();
        if (Objects.nonNull(properties)) {
            final String p = properties.getProperties();
            List<ItemProperty> deserialize = JacksonUtil.deserialize(p, new TypeReference<List<ItemProperty>>() {
            });

            deserialize.stream().forEach(property -> {
                Map<String, String> value = property.getValue();
                if (value.containsKey("value")) {
                    final ESProperty esProp = ESProperty.builder().value(value.get("value")).build();
                    esProperties.put(property.getName(), esProp);
                }
                
            });
        }
        return esProperties;
    }

    private  List<String> getItemCategories(Item item, List<ItemCategory> categories) {
        final List<String> categoryNames = Lists.newArrayList();
        final LinkedList<String> stack = new LinkedList<>();
        stack.addFirst(item.getCategory());
        while (!stack.isEmpty()) {
            final String name = stack.removeFirst();
            final ItemCategory itemCategory = categories.stream()
                    .filter(c -> name.equals(c.getName()))
                    .findFirst().orElse(null);
            if (Objects.nonNull(itemCategory)) {
                categoryNames.add(0, categoryMap.get(itemCategory.getParentId()).getName());
                stack.addLast(itemCategory.getParentId());
            }
        }
        return categoryNames;
    }

    private List<String> getItemCompositions(final Item item, final List<ItemComposition> compositions) {
        final List<String> ancestors = Lists.newArrayList();
        final LinkedList<List<String>> stack = new LinkedList<>();
        stack.addFirst(Lists.newArrayList(item.getId()));
        while (!stack.isEmpty()) {
            stack.removeFirst().forEach(s -> {
                final List<String> compositionIds = compositions.stream()
                        .filter(nextComposition -> Objects.nonNull(nextComposition.getCompositionItemId()))
                        .map(ItemComposition::getCompositionItemId).collect(toList());
                ancestors.addAll(compositionIds);
                stack.addLast(compositionIds);
            });

        }
        return ancestors;
    }

    private void createItemIndex(final String index, final List<EsItem> esItems) {
        /*if (!clientInterface.existIndice(index)) {
            final String createIndex = clientInterface.createIndiceMapping(index, "createIndex");
            System.out.println(createIndex);
        }*/
        clientInterface.addDocuments(index, esItems, "timeout=30m");
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        readCacheData();
        List<String> ids = Lists.newArrayList("f885d6b7-b6e5-4f9f-b1e3-313aa76e7c42",
                "eafe8bec-87cb-434e-a736-21a2d3181d7b", "6d8bf762-2a39-4b8e-96d3-2d42c9fb45d1",
                "eb5272ed-8951-4895-89ff-403d6a3e7ab9", "8b900c1b-e109-4dd0-a56f-01692c3f46c6");
        queryByIds(ids);
    }

    
    /**
     * 查询多个id
     */
    public void queryByIds(List<String> ids) {
        String docId1 = "f885d6b7-b6e5-4f9f-b1e3-313aa76e7c42";
        MetaMap metaMap = clientInterface.getDocument(INDEX, docId1, MetaMap.class);
        Map docMap = clientInterface.getDocument(INDEX, docId1, Map.class);
        System.out.println();
        clientInterface.searchList(INDEX, "{\"query\":{\"ids\":{\"values\":[" + ids + "]}}}", MetaMap.class);
        
        
        
        
    }

    /**
     * 查询所有数据 SCROLL
     * 查询所有数据，并指定返回tags和categories字段
     * 分页查找: 
     * 根据属性查找 : getItemByRepoIdAndPropertyAndValue
     * 根据ids获取数据
     * 获取单个、多个itemId的compositions ： 
     *      获取多个itemId的compositions: getCompositionByIds
     * 获取某个索引下所有包括categories属性的categories
     * 获取tags对应的数据 ： searchItemsByTags
     * 根据tags,itemIds或者categories查询数据 ； queryItemsByItemIdsOrCategories
     */
    public static void addDocument() {
        //直接指定文档id
//        String response = clientInterface.addDocumentWithId("索引表", demo,"文档id");
        System.out.println();
    }

    public static void deleteByIds() {
//        clientInterface.deleteDocument("索引表","索引类型", "文档id");
//        clientInterface.deleteDocuments("索引表","索引类型", new String[]{"文档id","文档id","文档id"});
        System.out.println();
    }
}
